#ifndef AES_H
#define AES_H

// https://tools.ietf.org/html/rfc3602
// https://www.cosic.esat.kuleuven.be/nessie/testvectors/bc/rijndael/rijndael.html

#include <stdbool.h>
#include <stdint.h>

#include "crypto/ciphers/common.h"

#ifdef __cplusplus
extern "C"
{
#endif

#define AES_MIN_KEY_SIZE       16
#define AES_MAX_KEY_SIZE       32
#define AES_KEYSIZE_128        16
#define AES_KEYSIZE_192        24
#define AES_KEYSIZE_256        32
#define AES_BLOCK_SIZE         16
#define AES_IV_SIZE            AES_BLOCK_SIZE
#define AES_MAX_KEYLENGTH      (15 * 16)
#define AES_MAX_KEYLENGTH_U32  (AES_MAX_KEYLENGTH / sizeof(u32))

/*
 * Please ensure that the first two fields are 16-byte aligned
 * relative to the start of the structure, i.e., don't move them!
 */
struct aes_ctx {
	u32 key_enc[AES_MAX_KEYLENGTH_U32];
	u32 key_dec[AES_MAX_KEYLENGTH_U32];
	u32 key_length;
	u8 iv[AES_IV_SIZE];
	u8 mode_state[AES_IV_SIZE];
	enum cipher_mode_t mode;
};

// Initializes a struct aes_ctx based on a key.
// The keylen expresses the length of the key in bytes.
// The key is padded to the maximum of 256 bits before round key generation.
// Any key length <= SERPENT_MAX_KEY_SIZE bytes = 256 bits = 32 bytes is allowed by the algorithm.
bool aes_init_context(struct aes_ctx *ctx, const u8 *key, int keylen, enum cipher_mode_t mode, const u8 *iv);

// Decrypts length bytes of ciphertext from src, writing the result in dst.
// The length of the ciphertext is equal to the length of the plaintext.
// The length will be rounded down to a multiple of the block size (SERPENT_BLOCK_SIZE bytes). If you must encrypt
// a plaintext whose length is not a multiple of the blocksize, you have to pad it beforehand.
// Returns the number of bytes encrypted.
i64 aes_encrypt_buffer(struct aes_ctx *ctx, u8 *dst, const u8 *src, i64 length);

// Decrypts length bytes of ciphertext from src, writing the result in dst.
// The length of the ciphertext is equal to the length of the plaintext.
// The length will be rounded down to a multiple of the block size (SERPENT_BLOCK_SIZE bytes).
// Returns the number of bytes decrypted.
i64 aes_decrypt_buffer(struct aes_ctx *ctx, u8 *dst, const u8 *src, i64 length);

// Encrypts one block of plaintext (SERPENT_BLOCK_SIZE bytes = 128 bits = 16 bytes), writing the result in dst.
// The length of the ciphertext is equal to the length of the plaintext.
// Normally you should not use this function; use serpent_encrypt_buffer instead.
void aes_encrypt_block(const struct aes_ctx *ctx, u8 *dst, const u8 *src);

// Decrypts one block of ciphertext (SERPENT_BLOCK_SIZE bytes = 128 bits = 16 bytes), writing the result in dst.
// The length of the ciphertext is equal to the length of the plaintext.
// Normally you should not use this function; use aes_decrypt_buffer instead.
void aes_decrypt_block(const struct aes_ctx *ctx, u8 *dst, const u8 *src);


#ifdef __cplusplus
}
#endif


#endif // AES_H
